/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package testing.jee.ejb;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import testing.jee.entity.User;
import org.apache.log4j.Logger;
import org.hibernate.SQLQuery;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import testing.jee.config.ConfigBean;
import testing.jee.entity.Config;
import testing.jee.entity.Timeline;
import testing.jee.entity.Workpackage;

/**
 *
 * @author nova
 */
@Repository
public class RequestBean {

    private final Logger logger = Logger.getLogger(RequestBean.class);
    SessionFactory sessionFactory;
    HibernateTemplate template;

    @Autowired
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
        template = new HibernateTemplate(sessionFactory, true);
    }

    @Transactional
    public Config getConfig() {
        List<Config> configBeans = template.loadAll(Config.class);
        if (configBeans.isEmpty()) {
            System.out.println("nothing found");
            Config cfg = new Config();
            cfg.setHoursPerDay(8);
            template.persist(cfg);
            return cfg;
        }
        System.out.println("something found");
        return (Config) configBeans.get(0);
    }

    @Transactional
    public void updateConfig(String id, int hoursPerDay) {
        Config cfg = template.get(Config.class, id);
        if (cfg != null) {
            cfg.setHoursPerDay(hoursPerDay);
            template.merge(cfg);
        }
    }

    @Transactional
    public void createUser(User user) {
        template.persist(user);
    }

    @Transactional(readOnly=true)
    public User findUser(String id) {
        User user = template.get(User.class, id);
        return user;
    }

    @Transactional(readOnly=true)
    public List<User> listUsers() {
        return template.find("from User");
    }

    @Transactional
    public void deleteUser(String id) {
        User user = template.get(User.class, id);
        if (user != null) {
            template.delete(user);
        }
    }

    @Transactional(readOnly=true)
    public List<String> suggestUserNames(String label) {
        return template.find("SELECT DISTINCT w.label FROM Users w WHERE LOWER(w.label) LIKE CONCAT(?, '%')", label.toLowerCase());
    }

    @Transactional
    public void createWorkpackage(Workpackage workpackage) {
        template.persist(workpackage);
    }

    @Transactional(readOnly=true)
    public String getWorkpackageLabelById(long id) {
        Workpackage workpackage = template.get(Workpackage.class, id);
        if (workpackage != null) {
            return workpackage.getLabel();
        } else {
            return "null";
        }
    }

    @Transactional(readOnly=true)
    public Timeline getTimeline(Date startDate, Date endDate, User user) {
        Timeline userTimeline = Timeline.getTimeline(startDate, endDate);
        userTimeline.setUser(user);
        List workpackages = template.find("from Workpackage w WHERE assignedDate >= ? AND assignedDate <= ? AND assignedUser = ?", startDate, endDate, user);
        if (workpackages != null) {
            for (Workpackage workpackage : (List<Workpackage>) workpackages) {
                userTimeline.getWorkday(new Date(workpackage.getAssignedDate().getTime())).addWorkpackage(workpackage);
            }
        }
        return userTimeline;
    }

    @Transactional(readOnly=true)
    public Collection<Timeline> getTimelines(Date startDate, Date endDate) {
        Map<String, Timeline> timelineMap = new HashMap<String, Timeline>();
        for (User user : listUsers()) {
            Timeline userTimeline = Timeline.getTimeline(startDate, endDate);
            userTimeline.setUser(user);
            timelineMap.put(user.getId(), userTimeline);
        }
        List workpackages = template.find("from Workpackage w WHERE assignedDate >= ? AND assignedDate <= ?", startDate, endDate);
        if (workpackages != null) {
            for (Workpackage workpackage : (List<Workpackage>) workpackages) {
                Timeline userTimeline = timelineMap.get(workpackage.getAssignedUser().getId());
                userTimeline.getWorkday(new Date(workpackage.getAssignedDate().getTime())).addWorkpackage(workpackage);
            }
        }
        return timelineMap.values();
    }

    @Transactional(readOnly=true)
    public List<Workpackage> listWorkpackages() {
        return template.find("from Workpackage w WHERE w.assignedUser is NULL order by w.dueDate ASC");
    }

    @Transactional(readOnly=true)
    public List<Workpackage> listWorkpackages(String orderBy, String orderDirection) {
        if ((orderBy.equals("w.dueDate") || orderBy.equals("w.label") || orderBy.equals("w.duration"))) {
            return template.find("from Workpackage w WHERE w.assignedUser is NULL order by " + orderBy + " " + orderDirection);
        } else {
            return listWorkpackages();
        }
    }

    @Transactional
    public void deleteWorkpackage(long id) {
        Workpackage workpackage = template.get(Workpackage.class, id);
        if (workpackage != null) {
            template.delete(workpackage);
        }
    }

    @Transactional(readOnly = true)
    public List<String> suggestWorkpackageNames(String label) {
        SQLQuery query = sessionFactory.getCurrentSession().createSQLQuery("SELECT w.label, COUNT(w.label) as labelcount FROM workpackage w GROUP BY w.label ORDER BY labelcount DESC");
        query.setMaxResults(10);
        ArrayList<String> returnList = new ArrayList<String>(10);
        for (Object o : query.list()) {
            Object[] row = (Object[]) o;
            returnList.add((String) row[0]);
        }
        return returnList;
    }

    @Transactional
    public Workpackage splitWorkpackage(int hours, long id) {
        Workpackage workpackage = template.get(Workpackage.class, id);
        if (workpackage != null) {
            if (workpackage.getDuration() <= hours) {
                return null;
            } else {
                if (workpackage.getAssignedUser() == null) {
                    long assocId = workpackage.getId();
                    if (workpackage.getAssocId() != 0) {
                        assocId = workpackage.getAssocId();
                    }
                    Workpackage firstChild = new Workpackage(workpackage);
                    firstChild.setAssocId(assocId);
                    firstChild.setDuration(hours);
                    Workpackage secondChild = new Workpackage(workpackage);
                    secondChild.setAssocId(assocId);
                    secondChild.setDuration(secondChild.getDuration() - hours);

                    template.persist(firstChild);
                    template.persist(secondChild);
                    template.delete(workpackage);
                    return firstChild;
                }
            }
        }
        return null;
    }

    @Transactional
    public void mergeWorkpackage(long id) {
        Workpackage workpackage = template.get(Workpackage.class, id);
        if (workpackage != null) {
            if (workpackage.getAssignedUser() == null) {
                if (workpackage.getAssocId() != 0) {
                    long assocId = workpackage.getAssocId();

                    List<Workpackage> associatedPackages = template.find("from Workpackage w WHERE assocId = ? AND assignedUser is NULL", assocId);
                    int accumulatedDuration = 0;
                    Workpackage newWorkpackage = new Workpackage(workpackage);

                    for (Workpackage wp : associatedPackages) {
                        accumulatedDuration += wp.getDuration();
                        template.delete(wp);
                    }
                    newWorkpackage.setDuration(accumulatedDuration);
                    template.persist(newWorkpackage);
                }
            }
        }
    }

    @Transactional
    public void mergeWorkpackages(long sourceId, long targetId) throws Exception {
        Workpackage mergeSource = template.get(Workpackage.class, sourceId);
        if (mergeSource == null) {
            throw new Exception("Source workpackage to merge must not be null");
        } else if (mergeSource.getAssignedUser() != null || mergeSource.getAssignedDate() != null) {
            throw new Exception("Source workpackage is already assigned and cannot be merged");
        }
        Workpackage mergeTarget = template.get(Workpackage.class, targetId);
        if (mergeTarget == null) {
            throw new Exception("Target workpackage to merge must not be null");
        } else if (mergeTarget.getAssignedUser() != null || mergeTarget.getAssignedDate() != null) {
            throw new Exception("Target workpackage is already assigned and cannot be merged");
        }
        if (mergeSource.getAssocId() == 0 || mergeTarget.getAssocId() == 0) {
            throw new Exception("Only splitted workpackages can be merged");
        }
        if (mergeTarget.getAssocId() != mergeSource.getAssocId()) {
            throw new Exception("The workpackages were not split from the same origin");
        }

        mergeTarget.setDuration(mergeTarget.getDuration() + mergeSource.getDuration());
        template.delete(mergeSource);
        template.merge(mergeTarget);
    }

    @Transactional
    public void unassignWorkpackage(long id) {
        Workpackage workpackage = template.get(Workpackage.class, id);
        if (workpackage != null && workpackage.getAssignedUser() != null) {
            workpackage.setAssignedUser(null);
            workpackage.setAssignedDate(null);
            template.merge(workpackage);
        }
    }

    @Transactional
    public void assignWorkpackage(long id, User user, Date date) {
        Workpackage workpackage = template.get(Workpackage.class, id);
        if (workpackage != null && workpackage.getAssignedUser() == null) {
            //if(workpackage.getDueDate().after(date)) {
            int duration = workpackage.getDuration();
            int free = getFreeDurationForDay(user, date);
            if (duration > free) {
                Workpackage splitFirst = splitWorkpackage(free, id);
                splitFirst.setAssignedDate(date);
                splitFirst.setAssignedUser(user);
                template.merge(splitFirst);
            } else {
                workpackage.setAssignedDate(date);
                workpackage.setAssignedUser(user);
                template.merge(workpackage);
            }
            //}
        }
    }

    @Transactional(readOnly=true)
    public int getFreeDurationForDay(User user, Date date) {
        List<Workpackage> packages = getWorkpackagesForDay(user, date);
        int hoursAssigned = 0;
        for (Workpackage wp : packages) {
            hoursAssigned += wp.getDuration();
        }
        return ConfigBean.HOURS_PER_DAY - hoursAssigned;
    }

    @Transactional(readOnly=true)
    public List<Workpackage> getWorkpackagesForDay(User user, Date date) {
        return template.find("from Workpackage w WHERE assignedDate = ? AND assignedUser = ?", date, user);
    }
}
